#include <iostream>
#include <math.h>
#include "Physical/pwrPlant.h"

//
// Static Variables
//
unsigned long physical::pwrPlant::POWER_PLANT_COUNT = 0;

//
// Constructors/Destructors
//  
/**
 * Empty Constructor
 */
physical::pwrPlant::pwrPlant ( ) :
        physical::property::property ( -1.0, -1.0 )
{
    initAttributes();
}

/**
 * Constructor including fuelCost (should be used)
 */
physical::pwrPlant::pwrPlant (float cost ) :
        physical::property::property ( -1.0, -1.0 )
{
    initAttributes();
    fuelCost = cost;
}

/**
 * Constructor including fuelCost (should be used)
 */
physical::pwrPlant::pwrPlant (float cost, double lattitude, double longitude ) :
        physical::property::property ( lattitude, longitude )
{
    initAttributes( );
    fuelCost = cost;
}

/**
  * Empty Destructor
  */
physical::pwrPlant::~pwrPlant ( )
{
    // If the vector has elements then delete each ptr element and clear the vector
    //  for incremental cost, cost, and the generator vectors
    if (incrementalCost.size() > 0)
    {
        for (unsigned int i = 0; i < incrementalCost.size(); i++)
        {
            delete [] incrementalCost[i];
        }
        incrementalCost.clear();
    }
    if (cost.size() > 0)
    {
        for (unsigned int i = 0; i < cost.size(); i++)
        {
            delete [] cost[i];
        }
        cost.clear();
    }
    if (generatorVector.size() > 0)
    {
        for (unsigned int i = 0; i < generatorVector.size(); i++)
        {
            delete [] generatorVector[i];
        }
        generatorVector.clear();
    }
    subStationLink = NULL;
}


//  
// Methods
//  
/**
 * Add generator to the plant from a generator object. Returns FALSE if error
 * occured, else returns TRUE.
 * Add a Generator object to the generatorVector List.
 * @return bool
 * @param  gen Generator object to be added to plant.
 */
bool physical::pwrPlant::addGenerator ( generator* add_object )
{
    // Must have fuel cost for basic generators
    if (fuelCost <= 0.0)
        return false;

    // Add new generator to generator ptr list
    generatorVector.push_back(add_object);

    // Add new generators cost curve constants to list
    float* prodCurve = add_object->getCurve();
    float* costCurve = new float[add_object->getOrder()+1];
    for (int i = 0; i <= add_object->getOrder(); i++)
    {
        costCurve[i] = fuelCost*(prodCurve[i]);
    }
    cost.push_back(costCurve);

    // Add new generators incremental cost curve constants to list
    float* icCurve = new float[add_object->getOrder()];
    for (int i = 0; i <= add_object->getOrder()-1; i++)
    {
        icCurve[i] = (i+1)*fuelCost*(prodCurve[i+1]);
    }
    incrementalCost.push_back(icCurve);

    // Increment the count of the generators in the plant
    incrementNumElectGens();

    if (generatorVector.back() == add_object)
        return true;
    else
        return false;
}

/**
 * Remove generator from the plant from a generator object. Returns FALSE if error
 * occured, else returns TRUE.
 * Remove a Generator object from generatorVector List
 * @return bool
 * @param  gen
 */
bool physical::pwrPlant::removeGenerator ( generator* remove_object )
{
    int i, size = generatorVector.size();
    generator* item;
    for ( i = 0; i < size; i++)
    {
        item = generatorVector.at(i);
        if(item == remove_object)
        {
            // Erase Generator from list
            std::vector<generator*>::iterator git = generatorVector.begin() + i;
            generatorVector.erase(git);
            // Erase Generator's cost curve
            std::vector<float*>::iterator fit1 = cost.begin() + i;
            float* costCurve = cost.at(i);
            delete [] costCurve;
            cost.erase(fit1);
            // Erase Generator's incremental cost curve
            std::vector<float*>::iterator fit2 = incrementalCost.begin() + i - 1;
            float* icCurve = cost.at(i - 1);
            delete [] icCurve;
            incrementalCost.erase(fit2);
            // Decrement the number of generators in the plant
            decrementNumElectGens();
            return true;
        }
    }
    if ((i == size)&&(item != remove_object))
    {
        return false;
    }
    return false;
}

/**
 * Get the list of Generator objects held by m_generatorVector
 * @return vector<generator *> list of Generator objects held by m_generatorVector
 */
std::vector<physical::generator*> physical::pwrPlant::getGeneratorList ( )
{
    return generatorVector;
}

/**
 * Returns the maximum electrical generation output of the plant.
 * @return float
 */
float physical::pwrPlant::getMaxElectOutput ( )
{
    int i, sum = 0, size = generatorVector.size();
    for ( i = 0; i < size; i++)
    {
        generator* item = generatorVector.at(i);
        sum += item->maxPowerOutput();
    }
    return sum;
}

/**
 * Returns the minimum electrical generation output of the plant.
 * @return float
 */
float physical::pwrPlant::getMinElectOutput ( )
{
    int i, sum = 0, size = generatorVector.size();
    for ( i = 0; i < size; i++)
    {
        generator* item = generatorVector.at(i);
        sum += item->minPowerOutput();
    }
    return sum;
}

/**
 * Modify the power plants electrical output. Returns TRUE if sucessful, else
 * returns FALSE (i.e. requested output out of range).
 * @return bool
 * @param  electOutput Electrical output of the power plant.
 */
bool physical::pwrPlant::setPlantOutput (double electOutput)
{
    //schedGenOutput(electOutput);
    if (electOutput == schedGenOutput (electOutput))
    {
        return true;
    }
    else
        return false;
}

/**
 * Connect power plant to substation (creates pointer from power plant to
 * substation object). Returns TRUE if successful, else FALSE.
 * @return bool
 * @param  subStation Pointer to substation object.
 */
bool physical::pwrPlant::connectStation (property* substation)
{
    if (substation != NULL)
    {
        this->setSubStation(substation);
        if (this->getSubStation() == substation)
            return true;
        else
            return false;
    }
    else
        return false;
}

/**
 * Disconnects power plant to substation (destroys pointer from power plant to
 * substation object). Returns TRUE if successful, else FALSE.
 * @return bool
 * @param  subStation Pointer to substation object.
 */
bool physical::pwrPlant::disconnectStation ()
{
    this->setSubStation(NULL);
    if (this->getSubStation() == NULL)
        return true;
    else
        return false;
}

/**
 * Print all information about power plant including all generators. Returns TRUE
 * if successfull, else FALSE.
 * @return bool
 */
bool physical::pwrPlant::printPlantInfo ( )
{
    // WORK TO BE DONE!!
        // Create/find output interface
            // Current:
            //   uniquePlantID, isOperating, fuelCost, electOutput, numElectGens
            //     for each gen: status, minPwr, maxPwr, prodCurve, costCurve, icCurve
            // Old?:
            //   isOperating, electOutput, fuelCost, total_costCurve, total_minPwr,
            //     total_maxPwr, total_startupCost, total_minDownTime, total_minUpTime,
            //     total_productionCurve
            //
    std::cout << "Unique plant ID is: " << getUniquePlantID() << std::endl;
    std::cout << "Status of Plant Operation: " << getIsPlantOperating() << std::endl;
    std::cout << "The Plant fuel cost is: " << getFuelCost() << std::endl;
    std::cout << "The electrical output is: " << getElectOutput() << std::endl;
    std::cout << "The number of generators is: " << getNumElectGens() << std::endl;
    std::cout << "    Generators: " << std::endl;
    for (int i = 0; i < getNumElectGens(); i++)
    {
        std::cout << "      Generator number " << i << ": " << std::endl;
        generator* genPtr = generatorVector.at(i);
        std::cout << "        Generator Status: " << genPtr->getGenStat() << std::endl;
        std::cout << "        Generator Minimum Power: " << genPtr->minPowerOutput() << std::endl;
        std::cout << "        Generator Maximum Power: " << genPtr->maxPowerOutput() << std::endl;
        std::cout << "        Generator Output: " << genPtr->getGenOutput() << std::endl;
        std::cout << "          Generator Production Curve:\t";
        float* curve = genPtr->getCurve();
        for (int j = 0; j <= genPtr->getOrder(); j++)
        {
            std::cout << curve[j] << "*P^" << j;
            if (j < genPtr->getOrder())
                std::cout << " + ";
        }
        std::cout << std::endl;
        std::cout << "          Cost Function:\t\t";
        for ( int j = 0; j <= genPtr->getOrder(); j++)
        {
            std::cout << (cost.at(i))[j] << "*P^" << j;
            if (j < genPtr->getOrder())
                std::cout << " + ";
        }
        std::cout << std::endl;
        std::cout << "          Incremental Cost Function:\t";
        for ( int j = 0; j <= genPtr->getOrder()-1; j++)
        {
            std::cout << (incrementalCost.at(i))[j] << "*P^" << j;
            if (j < genPtr->getOrder()-1)
                std::cout << " + ";
        }
        std::cout << std::endl;
        std::cout << "        End Generator " << i << std::endl;
    }
    return true;
}


//
// Protected methods
//
/**
  * Remove generator from the plant from a generator object. Returns FALSE if error
  * occured, else returns TRUE. INTERNAL USE ONLY.
  * @return bool
  * @param  gen Plant generator number [0..n-1].
  */
bool physical::pwrPlant::removeGen (int gen)
{
    int size = generatorVector.size();
    if (gen < size)
    {
        std::vector<generator*>::iterator it = generatorVector.begin() + gen;
        generatorVector.erase(it);
        return true;
    }
    else
        return false;
}

/**
 * Remove generator from the plant from a generator object. Returns FALSE if error
 * occured, else returns TRUE. INTERNAL USE ONLY.
 * @return bool
 * @param  gen Plant generator number [0..n-1].
 */
bool physical::pwrPlant::setGenON (int gen)
{
    if ((unsigned int)gen < generatorVector.size())
    {
        generator* item = generatorVector[gen];
        item->setGenOn();
        return true;
    }
    else
        return false;
}

/**
 * Begin generator shutdown sequence for generator number #'gen'. Returns FALSE if
 * error occured, else returns TRUE. INTERNAL USE ONLY.
 * @return bool
 * @param  gen Plant generator number [0..n-1].
 */
bool physical::pwrPlant::setGenOFF (int gen)
{
    int size = generatorVector.size();
    if (gen < size)
    {
        generator* item = generatorVector[gen];
        item->setGenOff();
        return true;
    }
    else
        return false;
}

/**
 * Schedule output of all operational generators in plant.
 * @return double
 * @param  output generator output value.
 */
double physical::pwrPlant::schedGenOutput (double output)
{
    /** WORK TO BE DONE!!
      // EQU1 = EQU2
      // EQU1 = EQU3
      // ...
      // output = VAR1 + VAR2 + VAR3 + ...
    // -> Assuming generators operating at the same incremental heat rate / cost
    // Start with n-1 equality equations and 1 variable equation where n equals the number of variables/generators
    // Eauality equations of order:  - solve for rhs variable
    //      {0,0}[-],{1,0}[y],{2,0}[y],{3,0}[n],{4,0}[n],{5,0}[n]
    //      {0,1}[y],{1,1}[y],{2,1}[y],{3,1}[n],{4,1}[n],{5,1}[n]
    //      {0,2}[y],{1,2}[y],{2,2}[y],{3,2}[n],{4,2}[n],{5,2}[n]
    //      {0,3}[n],{1,3}[n],{2,3}[n],{3,3}[n],{4,3}[n],{5,3}[n]
    //      {0,4}[n],{1,4}[n],{2,4}[n],{3,4}[n],{4,4}[n],{5,4}[n]
    //      {0,5}[n],{1,5}[n],{2,5}[n],{3,5}[n],{4,5}[n],{5,5}[n]
    // Variable equation with number of variables 1 to ?? - number of generators
      */
    int gens = getNumElectGens();
    if (gens < 1)
        return 0.0;
    if (gens == 1)
    {
        // TEMP
        std::cout << "ONLY ONE GENERATOR!?!?!?" << std::endl;
        generator* genPtr = generatorVector.at(0);
        genPtr->setGenOn();
        genPtr->scheduleGen(output);
    }
    else
    {
        generator* genPtrPri = generatorVector.at(0);
        generator* genPtrSec = generatorVector.at(1);
        double* constants = new double[gens];
        double* multipliers = new double[gens];
        for (int i = 0; i < gens; i++)
        {
            constants[i] = 0;
            multipliers[i] = 0;
        }
        double lhs = output;    // left-hand side of the variable sum equation
        double rhs = 1.0;       // right-hand side of the variable sum equation
        // Initialization for scheduling
        for (int i = 0; i < getNumElectGens()-1; i++)
        {
            float* c1 = genPtrPri->getCurve();
            float* c2 = genPtrSec->getCurve();
            // TEMP the order of the equ's must be the same currently
            switch(genPtrPri->getOrder())
            {
            case 1:
                // TEMP
                //std::cout << "1a-constants[" << i << "]:  " << constants[i] << std::endl;
                constants[i] = c1[0]/c2[1] - c2[0]/c2[1];
                // TEMP
                //std::cout << "c1" << c1 << std::endl;
                //std::cout << "*c1" << *c1 << std::endl;
                //std::cout << "c1[0]" << c1[0] << std::endl;
                // TEMP
                //std::cout << "c1[0]{" << c1[0] << "}/c2[1]{" << c2[1] << "} - c2[0]{";
                // TEMP
                //std::cout << c2[0] << "}/c2[1]{" << c2[1] << "};" << std::endl;
                // TEMP
                //std::cout << "1b-constants[" << i << "]:  " << constants[i] << std::endl;
                multipliers[i] = c1[1]/c2[1];
                break;
            case 2:
                // TEMP
                //std::cout << "2a-constants[" << i << "]:  " << constants[i] << std::endl;
                constants[i] = (pow(((c2[1]/(2*c2[2])) + c1[0]),-1.0/2.0)
                                + pow((c1[1]/(2*c1[2])),-1.0/2.0)
                                - pow(((c1[1]/(2*c1[2])) + c2[0]),-1.0/2.0)
                                + pow((c2[1]/(2*c2[2])),-1.0/2.0))/c2[2];
                // TEMP
                //std::cout << "2b-constants[" << i << "]:  " << constants[i] << std::endl;
                // TEMP
                //std::cout << "2a-multipliers[" << i << "]:  " << constants[i] << std::endl;
                multipliers[i] = c1[2]/c2[2];
                // TEMP
                // std::cout << "c1" << c1 << std::endl;
                // std::cout << "*c1" << *c1 << std::endl;
                // std::cout << "c1[0]" << c1[0] << std::endl;
                //std::cout << "c1[2]{" << c1[2] << "}/c2[2]{" << c2[2] << "}" << std::endl;
                // TEMP
                //std::cout << "2b-multipliers[" << i << "]:  " << constants[i] << std::endl;
                break;
            default:
                continue;
            }
            // TEMP
            //std::cout << "multipliers[" << i << "]: " << multipliers[i] << std::endl;
            //std::cout << "lhs " << lhs << std::endl;
            //std::cout << "rhs " << rhs << std::endl;
            lhs -= constants[i];
            rhs += multipliers[i];
            if (i+1 < getNumElectGens())
                genPtrSec = generatorVector.at(i+1);
        }
        // TEMP
        //std::cout << "lhs " << lhs << std::endl;
        //std::cout << "rhs " << rhs << std::endl;
        //std::cout << "lhs/rhs " << lhs/rhs << std::endl;
        // Scheduling calculations
        genPtrSec = generatorVector.front();
        if(!genPtrSec->scheduleGen(lhs/rhs)) // Schedule first generator
        {
            if ((lhs/rhs)<genPtrSec->minPowerOutput())
            {
                genPtrSec->scheduleGen(genPtrSec->minPowerOutput());
                //reschedule with [genList-gen1] and [load-gen1min]
            }
            else if ((lhs/rhs)>genPtrSec->maxPowerOutput())
            {
                genPtrSec->scheduleGen(genPtrSec->maxPowerOutput());
                //reschedule with [genList-gen1] and [load-gen1max]
            }
        }
        else
        {
            // Schedule the rest of the generators
            for (int i = 1; i < getNumElectGens(); i++)
            {
                genPtrSec = generatorVector.at(i);
                double setP = constants[i-1] + (lhs/rhs)*multipliers[i-1];
                if(!genPtrSec->scheduleGen(setP)) // Schedule first generator
                {
                    if (setP<genPtrSec->minPowerOutput())
                    {
                        genPtrSec->scheduleGen(genPtrSec->minPowerOutput());
                        //reschedule with [genList-gen1] and [load-gen1min]
                    }
                    else if (setP>genPtrSec->maxPowerOutput())
                    {
                        genPtrSec->scheduleGen(genPtrSec->maxPowerOutput());
                        //reschedule with [genList-gen1] and [load-gen1max]
                    }
                }
            }
        }
        delete [] constants;
        delete [] multipliers;
    }

    return getElectOutput();
}


//
// Accessor methods
//
/**
 * Get the value of POWER_PLANT_COUNT
 * Count of all power plants in the simulation. Only power plant constructors can
 * increment this variable. Nothing can devrement it. The result is a running count
 * of all power plants in the program instance.
 * @return the value of POWER_PLANT_COUNT
 */
unsigned long physical::pwrPlant::getPOWER_PLANT_COUNT ( )
{
    return physical::pwrPlant::POWER_PLANT_COUNT;
}

/**
 * Set the value of fuelCost
 * Monetary value of fuel used for power generation.
 * @param new_var the new value of fuelCost
 */
void physical::pwrPlant::setFuelCost ( float new_var )
{
    fuelCost = new_var;
}

/**
 * Get the value of fuelCost
 * Monetary value of fuel used for power generation.
 * @return the value of fuelCost
 */
float physical::pwrPlant::getFuelCost ( )
{
    return fuelCost;
}

/**
 * Set the value of isPlantOperating
 * If any of the generators are operating then this is TRUE, else FALSE.
 * @param new_var the new value of isPlantOperating
 */
void physical::pwrPlant::setIsPlantOperating ( bool new_var )
{
    isPlantOperating = new_var;
}

/**
 * Get the value of isPlantOperating
 * If any of the generators are operating then this is TRUE, else FALSE.
 * @return the value of isPlantOperating
 */
bool physical::pwrPlant::getIsPlantOperating ( )
{
    isPlantOperating = false;
    if (getNumElectGens() > 0)
    {
        for (int i = 0; i < getNumElectGens(); i++)
        {
            generator* genPtr = generatorVector.at(i);
            isPlantOperating |= genPtr->getGenStat();
        }
    }
    return isPlantOperating;
}

/**
 * Set the value of electOutput
 * Sum of electrical output from all generators in the power plant.
 * @param new_var the new value of electOutput
 */
void physical::pwrPlant::setElectOutput ( double new_var )
{
    electOutput = new_var;
}

/**
 * Get the value of electOutput
 * Sum of electrical output from all generators in the power plant.
 * @return the value of electOutput
 */
double physical::pwrPlant::getElectOutput ( )
{
    electOutput = 0.0;
    if (getNumElectGens() > 0)
    {
        for (int i = 0; i < getNumElectGens(); i++)
        {
            generator* genPtr = generatorVector.at(i);
            // TEMP
            //std::cout << "Generator " << i << " output is: " << genPtr->getGenOutput() << std::endl;
            electOutput += genPtr->getGenOutput();
            // TEMP
            //std::cout << "All Generator's output is " << electOutput << std::endl;
        }
    }
    return electOutput;
}

/**
 * Set the value of numElectGens
 * @param new_var the new value of numElectGens
 */
void physical::pwrPlant::setNumElectGens ( int new_var )
{
    numElectGens = new_var;
}

/**
 * Increment the value of numElectGens by one
 */
void physical::pwrPlant::incrementNumElectGens ( )
{
    numElectGens++;
}

/**
 * Decrement the value of numElectGens by one
 */
void physical::pwrPlant::decrementNumElectGens ( )
{
    numElectGens--;
}

/**
 * Get the value of numElectGens
 * @return the value of numElectGens
 */
int physical::pwrPlant::getNumElectGens ( )
{
    return numElectGens;
}

/**
 * Set the value of subStation
 * Pointer to substation object connecting to the rest of the regional network.
 * @param new_var the new value of subStation
 */
void physical::pwrPlant::setSubStation ( physical::property* new_var )
{
    subStationLink = new_var;
}

/**
 * Get the value of subStation
 * Pointer to substation object connecting to the rest of the regional network.
 * @return the value of subStation
 */
physical::property* physical::pwrPlant::getSubStation ( )
{
    return subStationLink;
}

/**
 * Set the value of uniquePlantID
 * Unique power plant ID. Cannot be modified after instance is created.
 * SHOULD THIS BE REMOVED?????
 * @param new_var the new value of uniquePlantID
 */
void physical::pwrPlant::setUniquePlantID ( unsigned long new_var )
{
    uniquePlantID = new_var;
}

/**
 * Get the value of uniquePlantID
 * Unique power plant ID. Cannot be modified after instance is created.
 * @return the value of uniquePlantID
 */
unsigned long physical::pwrPlant::getUniquePlantID ( )
{
    return uniquePlantID;
}


//
// Other methods
//  
void physical::pwrPlant::initAttributes ( ) {
    setFuelCost(0.00);
    if (incrementalCost.size() > 0)
    {
        for (unsigned int i = 0; i < incrementalCost.size(); i++)
        {
            delete [] incrementalCost[i];
        }
        incrementalCost.clear();
    }
    if (cost.size() > 0)
    {
        for (unsigned int i = 0; i < cost.size(); i++)
        {
            delete [] cost[i];
        }
        cost.clear();
    }
    setIsPlantOperating(false);
    setElectOutput(-1.00);
    setNumElectGens(0);
    if (generatorVector.size() > 0)
    {
        for (unsigned int i = 0; i < generatorVector.size(); i++)
        {
            delete [] generatorVector[i];
        }
        generatorVector.clear();
    }
    subStationLink = NULL;
    uniquePlantID = POWER_PLANT_COUNT++;
}

